/*	$OpenBSD: mount_nfsv23.c,v 1.17 1999/06/17 20:53:28 millert Exp $	*/
/*	$NetBSD: mount_nfsv23.c,v 1.12.4.1 1996/05/25 22:48:05 fvdl Exp $	*/

/*
 * Copyright (c) 1992, 1993, 1994
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Rick Macklem at The University of Guelph.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef lint
static char copyright[] =
"@(#) Copyright (c) 1992, 1993, 1994\n\
	The Regents of the University of California.  All rights reserved.\n";
#endif /* not lint */

#ifndef lint
#if 0
static char sccsid[] = "@(#)mount_nfsv23.c	8.11 (Berkeley) 5/4/95";
#else
static char rcsid[] = "$NetBSD: mount_nfsv23.c,v 1.12.4.1 1996/05/25 22:48:05 fvdl Exp $";
#endif
#endif /* not lint */

#include <sys/param.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <sys/vnode_if.h>
#include <syslog.h>

#include <rpc/rpc.h>
#include <rpc/pmap_clnt.h>
#ifdef sparc
#include <nfs/krpc.h>
#else
#include <rpc/pmap_prot.h>
#endif

#include <newnfs/nfs/rpcv2.h>
#include <newnfs/nfs/nfsproto.h>
#include <newnfs/nfs/nfsv4_errstr.h>
#include <des.h>
#define	NFSPROC_T	struct proc
#define	NFSSOCKADDR_T		mbuf_t
#define	KEYSCHED_T	des_key_schedule
#define	NFSRV_MAXFH	NFSX_V4FHMAX
#define _KERNEL
#include <newnfs/nfs/nfs.h>
#undef _KERNEL
#include <newnfs/nfsclient/nfsmount.h>

#include <arpa/inet.h>

#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "mntopts.h"

#define	ALTF_BG		0x1
#define ALTF_NOCONN	0x2
#define ALTF_DUMBTIMR	0x4
#define ALTF_INTR	0x8
#define	ALTF_SEC	0x10
#define ALTF_NFSV3	0x20
#define ALTF_RDIRPLUS	0x40
#define	ALTF_MNTUDP	0x80
#define	ALTF_RSIZE	0x100
#define	ALTF_WSIZE	0x200
#define ALTF_NFSV4	0x400
#define ALTF_SOFT	0x800
#define ALTF_TCP	0x1000
#define ALTF_PORT	0x2000
#define ALTF_NFSV2	0x4000
#define	ALTF_GSSNAME	0x8000
#define	ALTF_DIRECTIO	0x10000
#define	ALTF_ALLGSSNAME	0x20000
#define	ALTF_IOTHREADS	0x40000
#define	ALTF_AUTOFS	0x80000
#define	ALTF_BROWSE	0x100000
#define	ALTF_ACL	0x200000

const struct mntopt mopts[] = {
	MOPT_STDOPTS,
	MOPT_FORCE,
	MOPT_UPDATE,
	{ "bg", 0, ALTF_BG, 1 },
	{ "conn", 1, ALTF_NOCONN, 1 },
	{ "dumbtimer", 0, ALTF_DUMBTIMR, 1 },
	{ "intr", 0, ALTF_INTR, 1 },
	{ "sec", 0, ALTF_SEC, 1 },
	{ "nfsv3", 0, ALTF_NFSV3, 1 },
	{ "rdirplus", 0, ALTF_RDIRPLUS, 1 },
	{ "mntudp", 0, ALTF_MNTUDP, 1 },
	{ "soft", 0, ALTF_SOFT, 1 },
	{ "tcp", 0, ALTF_TCP, 1 },
	{ "port", 0, ALTF_PORT, 1 },
	{ "nfsv2", 0, ALTF_NFSV2, 1 },
	{ "rsize", 0, ALTF_RSIZE, 1 },
	{ "wsize", 0, ALTF_WSIZE, 1 },
	{ "nfsv4", 0, ALTF_NFSV4, 1 },
#ifdef notnow
	{ "princ", 0, ALTF_GSSNAME, 1 },
#endif
	{ "dio", 0, ALTF_DIRECTIO, 1 },
#ifdef notnow
	{ "allgssname", 0, ALTF_ALLGSSNAME, 1 },
#endif
	{ "iothreadcnt", 0, ALTF_IOTHREADS, 1 },
	{ "automounted", 0, ALTF_AUTOFS, 1 },
	{ "browse", 0, ALTF_BROWSE, 1 },
	{ "acl", 0, ALTF_ACL, 1 },
	{ NULL }
};

struct nfs_args nfsdefargs = {
	NFS_ARGSVERSION,
	(struct sockaddr *)0,
	sizeof (struct sockaddr_in),
	SOCK_DGRAM,
	0,
	(u_char *)0,
	0,
	NFSMNT_NFSV3,
	0,
	0,
	NFS_READDIRSIZE,
	10,
	NFS_RETRANS,
	NFS_MAXGRPS,
	NFS_DEFRAHEAD,
	0,
	0,
	(char *)0,
	0,
	0,
	0,
	0,
	0,
	(u_char *)0,
	0,
	(char *)0,
	0,
	(char *)0,
	0,
};

struct nfhret {
	u_long		stat;
	long		vers;
	long		auth;
	long		fhsize;
	u_char		nfh[NFSX_FHMAX];
};

char defaultkrbname[1024], svcname[1024];

#define	DEF_RETRY	10000
#define	BGRND	1
#define	ISBGRND	2
int retrycnt;
int opflags = 0;
int nfsproto = IPPROTO_UDP;
int mnttcp_ok = 1;
u_short port_no = 0;
int force2 = 0;
int force3 = 0;
int force4 = 0;

int	getnfsargs __P((char *, struct nfs_args *));
void	set_rpc_maxgrouplist __P((int));
__dead	void usage __P((void));
int	xdr_dir __P((XDR *, char *));
int	xdr_dir4 __P((XDR *, char *));
int	xdr_fh __P((XDR *, struct nfhret *));
int	xdr_fh4 __P((XDR *, struct nfhret *));

int
main(argc, argv)
	int argc;
	char *argv[];
{
	register int c;
	register struct nfs_args *nfsargsp;
	struct nfs_args nfsargs;
	struct nfsd_cargs ncd;
	struct hostent *hp;
	char myname[MAXHOSTNAMELEN];
	int mntflags, altflags, i, nfssvc_flag, num;
	char *name, *p, *spec, *cp, *cp2;
	int error = 0;

	defaultkrbname[0] = '\0';
	/*
	 * First, get our fully qualified host name, if possible.
	 */
	if (gethostname(myname, MAXHOSTNAMELEN) >= 0) {
		cp = strchr(myname, '.');
		i = strlen(myname);
		if (cp != NULL && (cp - myname) < (i - 1)) {
			cp = myname;
		} else {
			/*
			 * No domain on myname, so try looking it up.
			 */
			hp = gethostbyname(myname);
			if (hp != NULL) {
				cp = strchr(hp->h_name, '.');
				i = strlen(hp->h_name);
				if (cp != NULL && (cp - hp->h_name) < (i - 1))
					cp = hp->h_name;
				else
					cp = NULL;
			} else
				cp = NULL;
		}
		if (cp == NULL) {
			warnx("Can't get fully qualified host name");
		}
	}

	retrycnt = DEF_RETRY;
	mntflags = 0;
	altflags = 0;
	nfsargs = nfsdefargs;
	nfsargsp = &nfsargs;
	nfsargsp->krbnamelen = 0;
	nfsargsp->krbname = defaultkrbname;
	nfsargsp->dirlen = 0;
	nfsargsp->dirpath = NULL;
	nfsargsp->svcnamelen = 0;
	nfsargsp->svcname = NULL;
	nfsargsp->iothreadcnt = 0;
	while ((c = getopt(argc, argv,
	    "234AbB:cdI:ilo::R:r:sS:Tt:w:x:UD")) != EOF)
		switch (c) {
		case '4':
			if (force2 || force3)
				fprintf(stderr, "-2, -3 and -4 are mutually exclusive");
			if (mnttcp_ok == 0) {
				fprintf(stderr, "UDP isn't allowed for  NFSv4 - using TCP\n");
				mnttcp_ok = 1;
			}
			nfsargsp->sotype = SOCK_STREAM;
			nfsproto = IPPROTO_TCP;
			force4 = 1;
			nfsargsp->flags |= NFSMNT_NFSV4;
			nfsargsp->flags &= ~NFSMNT_NFSV3;
			break;
		case '3':
			if (force2 || force4)
				fprintf(stderr, "-2, -3 and -4 are mutually exclusive");
			force3 = 1;
			break;
		case '2':
			if (force3 || force4)
				fprintf(stderr, "-2, -3 and -4 are mutually exclusive");
			force2 = 1;
			nfsargsp->flags &= ~NFSMNT_NFSV3;
			break;
#ifdef notdef
		case 'a':
			num = strtol(optarg, &p, 10);
			if (*p || num < 0)
				fprintf(stderr, "illegal -a value -- %s", optarg);
			nfsargsp->readahead = num;
			nfsargsp->flags |= NFSMNT_READAHEAD;
			break;
#endif
		case 'A':
			nfsargsp->flags |= NFSMNT_ACL;
			break;
		case 'b':
			opflags |= BGRND;
			break;
		case 'B':
			num = strtol(optarg, &p, 10);
			if (*p || num < 0 || num > NFS_MAXRAHEAD) {
				fprintf(stderr, "illegal -B value -- %s", optarg);
			} else {
				nfsargsp->iothreadcnt = num;

				/* and set the readahead to the same number */
				nfsargsp->readahead = num;
				nfsargsp->flags |= NFSMNT_READAHEAD;
			}
			break;
		case 'c':
			nfsargsp->flags |= NFSMNT_NOCONN;
			break;
		case 'd':
			nfsargsp->flags |= NFSMNT_DUMBTIMR;
			break;
		case 'I':
			num = strtol(optarg, &p, 10);
			if (*p || num <= 0)
				fprintf(stderr, "illegal -I value -- %s", optarg);
			nfsargsp->readdirsize = num;
			nfsargsp->flags |= NFSMNT_READDIRSIZE;
			break;
		case 'i':
			nfsargsp->flags |= NFSMNT_INT;
			break;
		case 'l':
			nfsargsp->flags |= NFSMNT_RDIRPLUS;
			break;
#ifdef notnow
		case 'n':
			nfsargsp->flags |= NFSMNT_ALLGSSNAME;
			break;
#endif
		case 'o':
			getmntopts(optarg, mopts, &mntflags, &altflags);
			if (altflags & ALTF_BG)
				opflags |= BGRND;
			if (altflags & ALTF_ACL)
				nfsargsp->flags |= NFSMNT_ACL;
			if (altflags & ALTF_NOCONN)
				nfsargsp->flags |= NFSMNT_NOCONN;
			if (altflags & ALTF_DUMBTIMR)
				nfsargsp->flags |= NFSMNT_DUMBTIMR;
			if (altflags & ALTF_INTR)
				nfsargsp->flags |= NFSMNT_INT;
			if (altflags & ALTF_NFSV4) {
				if (force3 || force2)
					fprintf(stderr,"conflicting version options");
				if (mnttcp_ok == 0) {
					fprintf(stderr, "UDP isn't allowed for  NFSv4 - using TCP\n");
					mnttcp_ok = 1;
				}
				nfsargsp->sotype = SOCK_STREAM;
				nfsproto = IPPROTO_TCP;
				force4 = 1;
				nfsargsp->flags |= NFSMNT_NFSV4;
				nfsargsp->flags &= ~NFSMNT_NFSV3;
			}
			if (altflags & ALTF_NFSV3) {
				if (force2 || force4)
					fprintf(stderr,"conflicting version options");
				force3 = 1;
			}
			if (altflags & ALTF_NFSV2) {
				if (force3 || force4)
					fprintf(stderr,"conflicting version options");
				force2 = 1;
				nfsargsp->flags &= ~NFSMNT_NFSV3;
			}
			if (altflags & ALTF_RDIRPLUS)
				nfsargsp->flags |= NFSMNT_RDIRPLUS;
			if (altflags & ALTF_MNTUDP) {
				if (force4)
					fprintf(stderr, "UDP is not allowed for NFSv4 -- ignored\n");
				else
					mnttcp_ok = 0;
			}
			if (altflags & ALTF_SOFT)
				nfsargsp->flags |= NFSMNT_SOFT;
			if (altflags & ALTF_DIRECTIO)
				nfsargsp->flags |= NFSMNT_DIRECTIO;
#ifdef notnow
			if (altflags & ALTF_ALLGSSNAME)
				nfsargsp->flags |= NFSMNT_ALLGSSNAME;
#endif
			if (altflags & ALTF_TCP) {
				nfsargsp->sotype = SOCK_STREAM;
				nfsproto = IPPROTO_TCP;
			}
			if (altflags & ALTF_PORT)
				port_no = atoi(strstr(optarg, "port=") + 5);
			if (altflags & ALTF_RSIZE) {
				num = atoi(strstr(optarg, "rsize=") + 6);
				num *= 1024;
				if (num <= 0 || num > 1048576)
				    fprintf(stderr, "illegal rsize= value Ignored");
				else if (nfsargsp->flags & NFSMNT_RSIZE)
				    fprintf(stderr, "-r overrode rsize=");
				else {
					nfsargsp->rsize = num;
					nfsargsp->flags |= NFSMNT_RSIZE;
				}
			}
			if (altflags & ALTF_WSIZE) {
				num = atoi(strstr(optarg, "wsize=") + 6);
				num *= 1024;
				if (num <= 0 || num > 1048576)
				    fprintf(stderr, "illegal wsize= value Ignored");
				else if (nfsargsp->flags & NFSMNT_WSIZE)
				    fprintf(stderr, "-w overrode wsize=");
				else {
					nfsargsp->wsize = num;
					nfsargsp->flags |= NFSMNT_WSIZE;
				}
			}
			if (altflags & ALTF_SEC) {
				cp = strstr(optarg, "sec=") + 4;
				cp2 = strchr(cp, ',');
				if (cp2 != NULL)
					*cp2 = '\0';
				if (!strcmp(cp, "krb5"))
					nfsargsp->flags |= NFSMNT_KERB;
				else if (!strcmp(cp, "krb5i"))
					nfsargsp->flags |= (NFSMNT_KERB |
						NFSMNT_INTEGRITY);
				else if (!strcmp(cp, "krb5p"))
					nfsargsp->flags |= (NFSMNT_KERB |
						NFSMNT_PRIVACY);
				else if (strcmp(cp, "sys"))
					fprintf(stderr, "sec= option invalid, ignored");
				if (cp2 != NULL)
					*cp2 = ',';
			}
#ifdef notnow
			if (altflags & ALTF_GSSNAME) {
				cp = strstr(optarg, "princ=") + 6;
				cp2 = strchr(cp, ',');
				if (cp2 != NULL)
					*cp2 = '\0';
				if (strlen(cp) > 0 && strlen(cp) <= 100) {
					nfsargsp->krbnamelen = strlen(cp);
					nfsargsp->krbname = cp;
				} else {
					fprintf(stderr, "princ= option invalid, ignored");
				}
				if (cp2 != NULL)
					*cp2 = ',';
			}
#endif
			if (altflags & ALTF_IOTHREADS) {
				num = atoi(strstr(optarg, "iothreadcnt=") + 12);
				if (num < 0 || num > NFS_MAXRAHEAD) {
					fprintf(stderr, "illegal iothreadcnt value -- %s", optarg);
				} else {
					nfsargsp->iothreadcnt = num;
	
					/* and set the readahead to the same number */
					nfsargsp->readahead = num;
					nfsargsp->flags |= NFSMNT_READAHEAD;
				}
			}
			if (altflags & ALTF_AUTOFS) {
				nfsargsp->flags |= NFSMNT_AUTOM;
			}
			altflags = 0;
			break;
#ifdef notnow
		case 'P':
			cp = optarg;
			if (cp != NULL && strlen(cp) > 0 && strlen(cp) <= 100) {
				nfsargsp->krbnamelen = strlen(cp);
				nfsargsp->krbname = cp;
			} else {
				fprintf(stderr, "illegal -P value -- NULL");
			}
			break;
#endif
		case 'R':
			num = strtol(optarg, &p, 10);
			if (*p || num <= 0)
				fprintf(stderr, "illegal -R value -- %s", optarg);
			retrycnt = num;
			break;
		case 'r':
			num = strtol(optarg, &p, 10);
			num *= 1024;
			if (*p || num <= 0 || num > 1048576)
				fprintf(stderr, "illegal -r value -- %s", optarg);
			else if (nfsargsp->flags & NFSMNT_RSIZE)
				fprintf(stderr, "rsize= overrode -r");
			else {
				nfsargsp->rsize = num;
				nfsargsp->flags |= NFSMNT_RSIZE;
			}
			break;
		case 's':
			nfsargsp->flags |= NFSMNT_SOFT;
			break;
		case 'S':
			cp = optarg;
			if (!strcmp(cp, "krb5"))
				nfsargsp->flags |= NFSMNT_KERB;
			else if (!strcmp(cp, "krb5i"))
				nfsargsp->flags |= (NFSMNT_KERB |
					NFSMNT_INTEGRITY);
			else if (!strcmp(cp, "krb5p"))
				nfsargsp->flags |= (NFSMNT_KERB |
					NFSMNT_PRIVACY);
			else if (strcmp(cp, "sys"))
				fprintf(stderr, "sec= option invalid, ignored");
			break;
		case 'T':
			nfsargsp->sotype = SOCK_STREAM;
			nfsproto = IPPROTO_TCP;
			break;
		case 't':
			num = strtol(optarg, &p, 10);
			if (*p || num <= 0)
				fprintf(stderr, "illegal -t value -- %s", optarg);
			nfsargsp->timeo = num;
			nfsargsp->flags |= NFSMNT_TIMEO;
			break;
		case 'w':
			num = strtol(optarg, &p, 10);
			num *= 1024;
			if (*p || num <= 0 || num > 1048576)
				fprintf(stderr, "illegal -w value -- %s", optarg);
			else if (nfsargsp->flags & NFSMNT_WSIZE)
				fprintf(stderr, "wsize= overrode -w");
			else {
				nfsargsp->wsize = num;
				nfsargsp->flags |= NFSMNT_WSIZE;
			}
			break;
		case 'x':
			num = strtol(optarg, &p, 10);
			if (*p || num <= 0)
				fprintf(stderr, "illegal -x value -- %s", optarg);
			nfsargsp->retrans = num;
			nfsargsp->flags |= NFSMNT_RETRANS;
			break;
		case 'U':
			if (force4)
				fprintf(stderr, "UDP is not allowed for NFSv4 -- ignored\n");
			else
				mnttcp_ok = 0;
			break;
		case 'D':
			nfsargsp->flags |= NFSMNT_DIRECTIO;
			break;
		default:
			usage();
			break;
		}
	if (nfsargsp->flags & NFSMNT_ALLGSSNAME) {
		if ((nfsargsp->flags & NFSMNT_KERB) == 0)
		    fprintf(stderr, "-n (allgssname) can only be used with krb5[ip], option ignored");
		if (nfsargsp->krbname == NULL)
		    fprintf(stderr, "-n (allgssname) cannot be used with a NULL princ, option ignored");
		nfsargsp->flags &= ~NFSMNT_ALLGSSNAME;
	}
	if ((force2 || force3) && (nfsargsp->flags & NFSMNT_ACL)) {
		fprintf(stderr, "ACLs only supported for NFSv4, option ignored");
		nfsargsp->flags &= ~NFSMNT_ACL;
	}
	argc -= optind;
	argv += optind;

	if (argc != 2)
		usage();

	spec = *argv++;
	name = *argv;

	if (!getnfsargs(spec, nfsargsp))
		exit(1);
	if (mount(MOUNT_NEWNFS, name, mntflags, nfsargsp)) {
		if (errno == EOPNOTSUPP) {
			fprintf(stderr, "%s: Filesystem not supported by kernel", name);
		} else {
			const char *errstr;
	
			errstr = nfsv4_geterrstr(errno);
			if (errstr != NULL)
				errx(1, "%s: %s", name, errstr);
			else
				err(1, name);
		}
#if __DARWIN_VERS_1050 > 0
	} else if ((nfsargsp->flags & (NFSMNT_KERB | NFSMNT_AUTOM)) ==
	    NFSMNT_KERB) {
		if (force4 && fork() == 0) {
			/*
			 * For Leopard NFSv4 mounts using Kerberos, a kernel
			 * thread isn't sufficient for the renew thread, since
			 * it can't successfully do an upcall to the gssd. As
			 * such, we fork child here and send that down into the
			 * kernel to be the renew thread, via the nfssvc()
			 * syscall.
			 */
			(void)signal(SIGTERM, SIG_IGN);
			(void)signal(SIGHUP, SIG_IGN);
			(void)signal(SIGINT, SIG_IGN);
			(void)signal(SIGQUIT, SIG_IGN);
			if (nfssvc(NFSSVC_RENEWTHREAD, name) < 0)
				fprintf(stderr, "Can't start renew thread\n");
		}
		for (i = 0; i < nfsargsp->iothreadcnt; i++) {
			if (fork() == 0) {
				(void)signal(SIGTERM, SIG_IGN);
				(void)signal(SIGHUP, SIG_IGN);
				(void)signal(SIGINT, SIG_IGN);
				(void)signal(SIGQUIT, SIG_IGN);
				if (nfssvc(NFSSVC_BIOD, name) < 0)
					fprintf(stderr, "Can't start renew thread\n");
				break;
			}
		}
#endif
	}
	exit(0);
}

int
getnfsargs(spec, nfsargsp)
	char *spec;
	struct nfs_args *nfsargsp;
{
	register CLIENT *clp;
	struct hostent *hp;
	static struct sockaddr_in saddr;
	struct timeval pertry, try;
	enum clnt_stat clnt_stat;
	int so = RPC_ANYSOCK, i, nfsvers, mntvers, orgcnt, prog, portnum, ret;
	int nullrpcok;
	char *hostp, *delimp, *cp;
	u_short tport;
	static struct nfhret nfhret;
	static char nam[MNAMELEN + 1];

	strncpy(nam, spec, MNAMELEN);
	nam[MNAMELEN] = '\0';
	if ((delimp = strchr(spec, '@')) != NULL) {
		hostp = delimp + 1;
	} else if ((delimp = strchr(spec, ':')) != NULL) {
		hostp = spec;
		spec = delimp + 1;
	} else {
		warnx("no <host>:<dirpath> or <dirpath>@<host> spec");
		return (0);
	}
	*delimp = '\0';
	/*
	 * Handle an internet host address and reverse resolve it if
	 * doing Kerberos.
	 */
	if (inet_aton(hostp, &saddr.sin_addr) != 0) {
		if ((nfsargsp->flags & NFSMNT_KERB)) {
			if ((hp = gethostbyaddr((char *)&saddr.sin_addr.s_addr,
			    sizeof (u_long), AF_INET)) == NULL) {
				warnx("can't resolve hostname for address %s",
				    hostp);
				return (0);
			}
		}
	} else {
		hp = gethostbyname(hostp);
		if (hp == NULL) {
			warnx("can't resolve address for host %s", hostp);
			return (0);
		}
		memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length);
	}

	/*
	 * For Kerberos mounts, we must have a fully qualified server
	 * name and we need to create a service principal name from it.
	 */
	if ((nfsargsp->flags & NFSMNT_KERB)) {
		cp = strchr(hp->h_name, '.');
		if (cp == NULL || *(cp + 1) == '\0') {
			warnx("Krb5 server host not fqdn %s", hp->h_name);
			return (0);
		}
		ret = snprintf(svcname, 1024, "nfs/%s", hp->h_name);
		if (ret < 0 || ret >= 1024) {
			warnx("Couldn't create svcname");
			return (0);
		}
		nfsargsp->svcname = svcname;
		nfsargsp->svcnamelen = ret;
	}

	tport = port_no;
	if (force4) {
		nfsvers = NFS_VER4;
		mntvers = NFS_VER4;
		prog = RPCPROG_NFS;
		if (tport == 0)
			tport = NFS_PORT;
		portnum = htons(tport);
#ifdef DIRPATH
		if (nfsargsp->flags & NFSMNT_KERB) {
			nfsargsp->dirpath = spec;
			nfsargsp->dirlen = strlen(spec);
			/*
			 * Catch the special case of an empty spec.
			 */
			if (nfsargsp->dirlen == 0) {
				nfsargsp->dirpath = "/";
				nfsargsp->dirlen = 1;
			}
		}
#endif
	} else if (force2) {
		nfsvers = NFS_VER2;
		mntvers = RPCMNT_VER1;
		prog = RPCPROG_MNT;
		portnum = 0;
	} else {
		nfsvers = NFS_VER3;
		mntvers = RPCMNT_VER3;
		prog = RPCPROG_MNT;
		portnum = 0;
	}
	orgcnt = retrycnt;
	saddr.sin_family = AF_INET;

#ifdef DIRPATH
	if (nfsvers == NFS_VER4 && (nfsargsp->flags & NFSMNT_KERB)) {
		nfsargsp->fhsize = 0;
		/*
		 * For NFSv4, try a Null RPC against the NFS server first, just
		 * to make sure it is alive.
		 */
		saddr.sin_port = portnum;
		while (retrycnt > 0) {
			so = RPC_ANYSOCK;
			clp = clnttcp_create(&saddr, prog, nfsvers,
			    &so, 0, 0);
			if (clp == NULL) {
				if ((opflags & ISBGRND) == 0 &&
				    (nfsargsp->flags & NFSMNT_AUTOM) == 0)
					clnt_pcreateerror("Cannot Null RPC");
				if (retrycnt <= 0)
					return (0);
			} else {
				try.tv_sec = 10;
				try.tv_usec = 0;
				clnt_stat = clnt_call(clp, NFSPROC_NULL,
				    (xdrproc_t)xdr_void,
				    NULL, (xdrproc_t)xdr_void, NULL, try);
				clnt_destroy(clp);
				if (clnt_stat != RPC_SUCCESS) {
					if ((opflags & ISBGRND) == 0 &&
					    (nfsargsp->flags & NFSMNT_AUTOM) == 0)
						clnt_pcreateerror("Cannot Null RPC");
					if (retrycnt <= 0)
						return (0);
				} else
					break;
			}
			retrycnt--;
			sleep(5);
		}
	} else
#endif
    {
tryagain:
	nfhret.stat = EACCES;	/* Mark not yet successful */
	while (retrycnt > 0) {
		nullrpcok = 1;
		so = RPC_ANYSOCK;
		/*
		 * For NFSv4, try a Null RPC against the NFS server first, just
		 * to make sure it is alive.
		 */
		if (nfsvers == NFS_VER4) {
			saddr.sin_port = portnum;
			clp = clnttcp_create(&saddr, prog, nfsvers,
			    &so, 0, 0);
			if (clp == NULL) {
				if ((opflags & ISBGRND) == 0 &&
				    (nfsargsp->flags & NFSMNT_AUTOM) == 0)
					clnt_pcreateerror("Cannot Null RPC");
				nullrpcok = 0;
			} else {
				try.tv_sec = 10;
				try.tv_usec = 0;
				clnt_stat = clnt_call(clp, NFSPROC_NULL,
				    (xdrproc_t)xdr_void,
				    NULL, (xdrproc_t)xdr_void, NULL, try);
				if (clnt_stat != RPC_SUCCESS) {
					if ((opflags & ISBGRND) == 0 &&
					    (nfsargsp->flags & NFSMNT_AUTOM) == 0)
					    warnx("%s", clnt_sperror(clp,
						"bad NULL RPC"));
					nullrpcok = 0;
				}
				clnt_destroy(clp);
			}
		}
		saddr.sin_family = AF_INET;
		saddr.sin_port = htons(PMAPPORT);
		if (nullrpcok && tport == 0)
			tport = pmap_getport(&saddr, RPCPROG_NFS, nfsvers,
			    nfsargsp->sotype == SOCK_STREAM ?
			    IPPROTO_TCP : IPPROTO_UDP);
		if (nullrpcok && tport == 0) {
			if ((opflags & ISBGRND) == 0 &&
			    (nfsargsp->flags & NFSMNT_AUTOM) == 0)
				clnt_pcreateerror("NFS Portmap");
		} else if (nullrpcok) {
			so = RPC_ANYSOCK;
			saddr.sin_port = portnum;
			pertry.tv_sec = 10;
			pertry.tv_usec = 0;
			if (mnttcp_ok && nfsargsp->sotype == SOCK_STREAM)
			    clp = clnttcp_create(&saddr, prog, mntvers,
				&so, 0, 0);
			else
			    clp = clntudp_create(&saddr, prog, mntvers,
				pertry, &so);
			if (clp == NULL) {
				if ((opflags & ISBGRND) == 0 &&
				    (nfsargsp->flags & NFSMNT_AUTOM) == 0)
					clnt_pcreateerror("Cannot MNT RPC");
			} else {
				clp->cl_auth = authunix_create_default();
				try.tv_sec = 10;
				try.tv_usec = 0;
				nfhret.auth = RPCAUTH_UNIX;
				nfhret.vers = mntvers;
				if (nfsvers == NFS_VER4) {
				  clnt_stat = clnt_call(clp, NFSV4PROC_COMPOUND,
				    xdr_dir4, spec, xdr_fh4, &nfhret, try);
				} else {
				  clnt_stat = clnt_call(clp, RPCMNT_MOUNT,
				    xdr_dir, spec, xdr_fh, &nfhret, try);
				}
				if (clnt_stat != RPC_SUCCESS) {
					if (clnt_stat == RPC_PROGVERSMISMATCH) {
						if (nfsvers == NFS_VER4 &&
						    !force4) {
							retrycnt = orgcnt;
							nfsvers = NFS_VER3;
							mntvers = RPCMNT_VER3;
							prog = RPCPROG_MNT;
							portnum = 0;
							tport = port_no;
							nfsargsp->flags &=
								~NFSMNT_NFSV4;
							nfsargsp->flags |=
								NFSMNT_NFSV3;
							auth_destroy(clp->cl_auth);
							clnt_destroy(clp);
							goto tryagain;
						} else if (nfsvers==NFS_VER3 &&
						    !force3) {
							retrycnt = orgcnt;
							nfsvers = NFS_VER2;
							mntvers = RPCMNT_VER1;
							prog = RPCPROG_MNT;
							portnum = 0;
							tport = port_no;
							nfsargsp->flags &=
								~NFSMNT_NFSV3;
							auth_destroy(clp->cl_auth);
							clnt_destroy(clp);
							goto tryagain;
						} else if ((nfsargsp->flags & NFSMNT_AUTOM) == 0) {
							warnx("%s",
							    clnt_sperror(clp,
							    "MNT RPC"));
						}
					}
					if ((opflags & ISBGRND) == 0 &&
					    (nfsargsp->flags & NFSMNT_AUTOM) == 0)
						warnx("%s", clnt_sperror(clp,
						    "bad MNT RPC"));
				} else {
					retrycnt = 0;
				}
				auth_destroy(clp->cl_auth);
				clnt_destroy(clp);
			}
		}
		if (--retrycnt > 0) {
			if (opflags & BGRND) {
				opflags &= ~BGRND;
				if (i = fork()) {
					if (i == -1)
						err(1, "nfs 2");
					exit(0);
				}
				(void) setsid();
				(void) close(STDIN_FILENO);
				(void) close(STDOUT_FILENO);
				(void) close(STDERR_FILENO);
				(void) chdir("/");
				opflags |= ISBGRND;
			}
			sleep(5);
		}
	}
	if (nfhret.stat) {
		if (opflags & ISBGRND)
			exit(1);
		errno = nfhret.stat;
		warnx("can't access %s: %s", spec, strerror(nfhret.stat));
		return (0);
	}
	nfsargsp->fh = nfhret.nfh;
	nfsargsp->fhsize = nfhret.fhsize;
    }
	saddr.sin_port = htons(tport);
	nfsargsp->addr = (struct sockaddr *) &saddr;
	nfsargsp->addrlen = sizeof (saddr);
	nfsargsp->hostname = nam;
	return (1);
}

/*
 * xdr routines for mount rpc's
 */
int
xdr_dir(xdrsp, dirp)
	XDR *xdrsp;
	char *dirp;
{
	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
}

int
xdr_dir4(xdrsp, dirp)
	XDR *xdrsp;
	char *dirp;
{
	register int i, namecnt;
	register char *ncp;
	unsigned long l;
	char *cp;

	/*
	 * For V4, we must create a compound RPC request which has a
	 * putrootfh op, lookup op(s) for each component and a getfh.
	 */
	namecnt = 0;
	ncp = dirp;
	do {
		while (*ncp == '/')
			ncp++;
		if (*ncp)
			namecnt++;
		while (*ncp && *ncp != '/')
			ncp++;
	} while (*ncp);
	cp = "Mount";
	if (!xdr_string(xdrsp, &cp, 100))
		return (0);
	l = NFSV4_MINORVERSION;
	if (!xdr_u_long(xdrsp, &l))
		return (0);
	l = namecnt + 2;
	if (!xdr_u_long(xdrsp, &l))
		return (0);
	l = NFSV4OP_PUTROOTFH;
	if (!xdr_u_long(xdrsp, &l))
		return (0);
	cp = dirp;
	for (i = 0; i < namecnt; i++) {
		l = NFSV4OP_LOOKUP;
		if (!xdr_u_long(xdrsp, &l))
			return (0);
		while (*cp == '/')
			cp++;
		ncp = cp;
		while (*ncp && *ncp != '/')
			ncp++;
		if (*ncp == '/')
			*ncp = '\0';
		if (!xdr_string(xdrsp, &cp, 1024))
			return (0);
		cp = ncp + 1;
	}
	l = NFSV4OP_GETFH;
	return (xdr_u_long(xdrsp, &l));
}

int
xdr_fh(xdrsp, np)
	XDR *xdrsp;
	register struct nfhret *np;
{
	register int i;
	long auth, authcnt, authfnd = 0;

	if (!xdr_u_long(xdrsp, &np->stat))
		return (0);
	if (np->stat)
		return (1);
	switch (np->vers) {
	case 1:
		np->fhsize = NFSX_V2FH;
		return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH));
	case 3:
		if (!xdr_long(xdrsp, &np->fhsize))
			return (0);
		if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX)
			return (0);
		if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
			return (0);
		if (!xdr_long(xdrsp, &authcnt))
			return (0);
		for (i = 0; i < authcnt; i++) {
			if (!xdr_long(xdrsp, &auth))
				return (0);
			if (auth == np->auth)
				authfnd++;
		}
		/*
		 * Some servers, such as DEC's OSF/1 return a nil authenticator
		 * list to indicate RPCAUTH_UNIX.
		 */
		if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX))
			np->stat = EAUTH;
		return (1);
	};
	return (0);
}

int
xdr_fh4(xdrsp, np)
	XDR *xdrsp;
	register struct nfhret *np;
{
	register int i;
	long l;
	unsigned long op;
	char str[1024], *cp;

	if (!xdr_u_long(xdrsp, &np->stat))
		return (0);
	if (np->stat)
		return (1);
	cp = str;
	if (!xdr_string(xdrsp, &cp, 1024))
		return (0);
	if (!xdr_long(xdrsp, &l))
		return (0);
	for (i = 0; i < l; i++) {
		if (!xdr_u_long(xdrsp, &op))
			return (0);
		if (!xdr_u_long(xdrsp, &np->stat))
			return (0);
		if (np->stat)
			return (1);
	}
	if (!xdr_long(xdrsp, &np->fhsize))
		return (0);
	if (np->fhsize <= 0 || np->fhsize > NFSX_V4FHMAX)
		return (0);
	if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
		return (0);
	return (1);
}

__dead void
usage()
{
	(void)fprintf(stderr, "usage: mount_newnfs %s\n%s\n%s\n%s\n",
"[-234AbcdDiIlsTU] [-a maxreadahead]",
"\t[-o options] [-R retrycnt] [-S authentication_type]",
"\t[-r readsize] [-t timeout] [-w writesize] [-x retrans]",
"\trhost:path node");
	exit(1);
}
